// Copyright (c) 2008 Michael Buckley

// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:

// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.

#import "BTLTCPSocketTests.h"
#import "BTLTCPSocketTestDelegate.h"

@implementation BTLTCPSocketTests

- (void)testTCPIPv4Socket
{
	BTLSocketIPAddress* addr1 = [BTLSocketIPAddress addressWithHostname:@"localhost" port:0 family:AF_INET error:nil];
	BTLSocketIPAddress* addr2 = [BTLSocketIPAddress addressWithHostname:@"127.0.0.1" port:0 family:AF_INET error:nil];
	BTLSocketIPAddress* addr3 = [BTLSocketIPAddress addressWithHostname:@"169.0.0.1" port:33345 family:AF_INET error:nil];
	[self testManagedTCPSocketWithSourceAddress:addr1 withDestinationAddress:addr2 badAddress:addr3];
	[self testUnmanagedTCPSocketWithSourceAddress:addr1 withDestinationAddress:addr2 badAddress:addr3];
	[self testUnmanagedDelegatelessTCPSocketWithSourceAddress:addr1 withDestinationAddress:addr2]; // Socket timeout does not apply to unmanaged, delegateless sockets.
}

- (void)testTCPIPv6Socket
{
	BTLSocketIPAddress* addr1 = [BTLSocketIPAddress addressWithHostname:@"localhost" port:0 family:AF_INET6 error:nil];
	BTLSocketIPAddress* addr2 = [BTLSocketIPAddress addressWithHostname:@"::1" port:0 family:AF_INET6 error:nil];
	BTLSocketIPAddress* addr3 = [BTLSocketIPAddress addressWithHostname:@"FE80::1" port:33345 family:AF_INET6 error:nil];
	[self testManagedTCPSocketWithSourceAddress:addr1 withDestinationAddress:addr2 badAddress:addr3];
	[self testUnmanagedTCPSocketWithSourceAddress:addr1 withDestinationAddress:addr2 badAddress:addr3];
	[self testUnmanagedDelegatelessTCPSocketWithSourceAddress:addr1 withDestinationAddress:addr2];
}

- (void)testManagedTCPSocketWithSourceAddress:(BTLSocketAddress*)addr1 withDestinationAddress:(BTLSocketAddress*)addr2 badAddress:(BTLSocketAddress*)badAddress
{	
	if(addr1 == nil || addr2 == nil){
		return;
	}
	
	BTLSocketManager* testManager = [BTLSocketManager new];
	
	BTLTCPSocketTestDelegate* testDelegate1 = [BTLTCPSocketTestDelegate new];
	BTLTCPSocketTestDelegate* testDelegate2 = [BTLTCPSocketTestDelegate new];
	
	BTLTCPSocket* sock1 = (BTLTCPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_STREAM];
	STAssertTrue([sock1 bindToAddress:addr1], nil);
	[sock1 setDelegate:testDelegate1];
	[sock1 setManager:testManager];
	
	BTLTCPSocket* sock2 = (BTLTCPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_STREAM];
	STAssertTrue([sock2 bindToAddress:addr2], nil);
	[sock2 setDelegate:testDelegate2];
	[sock2 setManager:testManager];
	STAssertTrue([sock2 listenWithBacklog:5], nil);
	
	[testDelegate1 setForbiddenAddress:[sock2 localAddress]];
	
	NSNumber* timeout = [[NSNumber alloc] initWithDouble:200.0];
	STAssertFalse([sock1 connectToAddress:[sock2 localAddress] withTimeout:timeout], nil);
	STAssertTrue([testDelegate1 connectionFailed], nil);
	STAssertFalse([testDelegate2 currentlyConnected], nil);
	[testDelegate1 setForbiddenAddress:nil];
	
	[sock1 connectToAddress:badAddress withTimeout:[NSNumber numberWithDouble:5.0]];
	while([testDelegate1 connectionFailed] != YES){
		[testManager select];
	}
	
	[sock1 connectToAddress:[sock2 localAddress] withTimeout:timeout];
	[testManager select];
	
	BTLTCPSocket* sock3 = (BTLTCPSocket*) [testDelegate2 lastSocket];
	
	while(sock3 == nil){
		[testManager select];
		sock3 = (BTLTCPSocket*) [testDelegate2 lastSocket];
	}
	[sock3 retain];
	
	[testManager select];
	
	while(![sock1 isConnected]){
		[testManager select];
		STAssertFalse([testDelegate1 connectionFailed], nil);
	}
	
	STAssertTrue([sock1 isConnected], nil);
	STAssertFalse([sock2 isConnected], nil);
	STAssertTrue([sock3 isConnected], nil);
	STAssertTrue([testDelegate1 currentlyConnected], nil);
	STAssertTrue([testDelegate2 currentlyConnected], nil);
	
	BTLSocketBuffer* data1 = [BTLSocketBuffer new];
	[data1 addData:"Test String" ofSize:12];
	[sock1 writeData:data1 toAddress:nil];
	
	[testManager select];
	
	STAssertEquals(testDelegate2, [sock3 delegate], nil);
	
	BTLSocketBuffer* testData = [testDelegate2 lastMessage];
	
	while(testData == nil){
		[testManager select];
		testData = [testDelegate2 lastMessage];
	}
	
	STAssertNotNil(testData, nil);
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"First data not equal", nil);
	}
	
	[sock3 writeData:data1 toAddress:nil];
	[testManager select];
	
	testData = nil;
	testData = [testDelegate1 lastMessage];
	
	while(testData == nil){
		[testManager select];
		testData = [testDelegate1 lastMessage];
	}
	
	STAssertNotNil(testData, nil);
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"Second data not equal");
	}
	
	srand(time(0));
	char buffer[3000];
	int i;
	for(i = 0; i < 3000; ++i){
		buffer[i] = rand() % 256; // Who cares about mod bias?
	}
	
	[data1 release];
	
	data1 = [BTLSocketBuffer new];
	[data1 addData:buffer ofSize:3000];
	[sock1 writeData:data1 toAddress:nil];
	
	[testManager select];
	testData = [testDelegate2 lastMessage];
	
	while(testData == nil){
		[testManager select];
		testData = [testDelegate2 lastMessage];
	}
	
	STAssertEquals([testData size], [data1 size], nil);
	
	const char* result = [testData rawData];
	for(i = 0; i < 3000; ++i){
		if(result[i] != buffer[i]){
			STFail(@"Char buffers not equal at iteration %d", i, nil);
			break;
		}
	}
	
	[sock1 closeConnectionToAddress:nil];
	STAssertFalse([testDelegate1 currentlyConnected], nil);
	[testManager select];
	STAssertTrue([testDelegate2 remoteDisconnected], nil);
	[sock3 connectionInterruptedToAddress:nil];
	STAssertFalse([testDelegate2 currentlyConnected], nil);
	
	[data1 release];
	[timeout release];
	[sock3 release];
	[sock2 release];
	[sock1 release];
	[testManager release];
	[testDelegate2 release];
	[testDelegate1 release];
}

- (void)testUnmanagedTCPSocketWithSourceAddress:(BTLSocketAddress*)addr1 withDestinationAddress:(BTLSocketAddress*)addr2 badAddress:(BTLSocketAddress*)badAddress
{
	if(addr1 == nil || addr2 == nil){
		return;
	}
	
	BTLTCPSocketTestDelegate* testDelegate1 = [BTLTCPSocketTestDelegate new];
	BTLTCPSocketTestDelegate* testDelegate2 = [BTLTCPSocketTestDelegate new];
	
	BTLTCPSocket* sock1 = (BTLTCPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_STREAM];
	STAssertTrue([sock1 bindToAddress:addr1], nil);
	[sock1 setDelegate:testDelegate1];
	
	BTLTCPSocket* sock2 = (BTLTCPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_STREAM];
	STAssertTrue([sock2 bindToAddress:addr2], nil);
	
	STAssertTrue([sock2 listenWithBacklog:5], nil);
	[testDelegate1 setForbiddenAddress:[sock2 localAddress]];
	
	NSNumber* timeout = [[NSNumber alloc] initWithDouble:200.0];
	STAssertFalse([sock1 connectToAddress:[sock2 localAddress] withTimeout:timeout], nil);
	STAssertTrue([testDelegate1 connectionFailed], nil);
	STAssertFalse([testDelegate2 currentlyConnected], nil);
	[testDelegate1 setForbiddenAddress:nil];
	
	[sock1 connectToAddress:badAddress withTimeout:[NSNumber numberWithDouble:5.0]];
	while([testDelegate1 connectionFailed] != YES){
		[sock1 connectToAddress:nil withTimeout:nil];
	}
	
	[sock1 connectToAddress:[sock2 localAddress] withTimeout:timeout];
	BTLTCPSocket* sock3 = (BTLTCPSocket*) [sock2 accept];
	[sock1 connectToAddress:nil withTimeout:nil];
	
	while(sock3 == nil){
		sock3 = (BTLTCPSocket*) [sock2 accept];
	}
	
	[sock3 setDelegate:testDelegate2];
	if([[sock3 delegate] respondsToSelector:@selector(connectionOpenedToAddress:sender:)]){
		[[sock3 delegate] connectionOpenedToAddress:[sock3 remoteAddress] sender:sock3];
	}
	
	while(![sock1 isConnected]){
		[sock1 connectToAddress:nil withTimeout:nil];
	}
	
	STAssertTrue([sock1 isConnected], nil);
	STAssertFalse([sock2 isConnected], nil);
	STAssertTrue([sock3 isConnected], nil);
	STAssertTrue([testDelegate1 currentlyConnected], nil);
	STAssertTrue([testDelegate2 currentlyConnected], nil);
	
	BTLSocketBuffer* data1 = [BTLSocketBuffer new];
	[data1 addData:"Test String" ofSize:12];
	[sock1 writeData:data1 toAddress:nil];
	
	[sock3 read];
	
	STAssertEquals(testDelegate2, [sock3 delegate], nil);
	
	BTLSocketBuffer* testData = [testDelegate2 lastMessage];
	
	while(testData == nil){
		[sock3 read];
		testData = [testDelegate2 lastMessage];
	}
	
	STAssertNotNil(testData, nil);
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"First data not equal", nil);
	}
	
	[sock3 writeData:data1 toAddress:nil];
	[sock1 read];
	
	testData = nil;
	testData = [testDelegate1 lastMessage];
	
	while(testData == nil){
		[sock1 read];
		testData = [testDelegate1 lastMessage];
	}
	
	STAssertNotNil(testData, nil);
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"Second data not equal");
	}
	
	[sock1 closeConnectionToAddress:nil];
	STAssertFalse([testDelegate1 currentlyConnected], nil);
	[sock3 read];
	STAssertTrue([testDelegate2 remoteDisconnected], nil);
	[sock3 connectionInterruptedToAddress:nil];
	STAssertFalse([testDelegate2 currentlyConnected], nil);
	
	[timeout release];
	[sock3 release];
	[sock2 release];
	[sock1 release];
	[testDelegate2 release];
	[testDelegate1 release];
}

- (void)testUnmanagedDelegatelessTCPSocketWithSourceAddress:(BTLSocketAddress*)addr1 withDestinationAddress:(BTLSocketAddress*)addr2
{
	if(addr1 == nil || addr2 == nil){
		return;
	}
	
	BTLTCPSocket* sock1 = (BTLTCPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_STREAM];
	STAssertTrue([sock1 bindToAddress:addr1], nil);
	
	BTLTCPSocket* sock2 = (BTLTCPSocket*) [BTLSocket socketWithAddressFamily:[addr1 family] type:SOCK_STREAM];
	STAssertTrue([sock2 bindToAddress:addr2], nil);
	
	STAssertTrue([sock2 listenWithBacklog:5], nil);
	
	NSNumber* timeout = [[NSNumber alloc] initWithDouble:200.0];
	
	[sock1 connectToAddress:[sock2 localAddress] withTimeout:timeout];
	BTLTCPSocket* sock3 = (BTLTCPSocket*) [sock2 accept];
	[sock1 connectToAddress:nil withTimeout:nil];
	
	while(sock3 == nil){
		sock3 = (BTLTCPSocket*) [sock2 accept];
	}
	
	while(![sock1 isConnected]){
		[sock1 connectToAddress:nil withTimeout:nil];
	}
	
	STAssertTrue([sock1 isConnected], nil);
	STAssertFalse([sock2 isConnected], nil);
	STAssertTrue([sock3 isConnected], nil);
	
	BTLSocketBuffer* data1 = [BTLSocketBuffer new];
	[data1 addData:"Test String" ofSize:12];
	[sock1 writeData:data1 toAddress:nil];
	
	BTLSocketBuffer* testData = [sock3 read];
	
	while(testData == nil){
		testData = [sock3 read];
	}
	
	STAssertNotNil(testData, nil);
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"First data not equal", nil);
	}
	
	testData = nil;
	[sock3 writeData:data1 toAddress:nil];
	testData = [sock1 read];
	
	while(testData == nil){
		testData = [sock1 read];
	}
	
	STAssertNotNil(testData, nil);
	
	if(strcmp([testData rawData], [data1 rawData]) != 0){
		STFail(@"Second data not equal");
	}
	
	[sock1 closeConnectionToAddress:nil];
	[sock3 connectionInterruptedToAddress:nil];
	
	[data1 release];
	[timeout release];
	[sock3 release];
	[sock2 release];
	[sock1 release];
}

@end
